/***************************************************************************/
/*                                                                         */
/*  ttbdf.c                                                                */
/*                                                                         */
/*    TrueType and OpenType embedded BDF properties (body).                */
/*                                                                         */
/*  Copyright 2005, 2006 by                                                */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used,       */
/*  modified, and distributed under the terms of the FreeType project      */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/***************************************************************************/


#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_TAGS_H
#include "ttbdf.h"

#include "sferrors.h"


#ifdef TT_CONFIG_OPTION_BDF

/*************************************************************************/
/*                                                                       */
/* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
/* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
/* messages during execution.                                            */
/*                                                                       */
    #undef  FT_COMPONENT
    #define FT_COMPONENT trace_ttbdf


FT_LOCAL_DEF(void)
tt_face_free_bdf_props(TT_Face face)
{
    TT_BDF bdf = &face->bdf;


    if (bdf->loaded)
    {
        FT_Stream stream = FT_FACE(face)->stream;


        if (bdf->table != NULL)
            FT_FRAME_RELEASE(bdf->table);

        bdf->table_end = NULL;
        bdf->strings = NULL;
        bdf->strings_size = 0;
    }
}


static FT_Error
tt_face_load_bdf_props(TT_Face face,
                       FT_Stream stream)
{
    TT_BDF bdf = &face->bdf;
    FT_ULong length;
    FT_Error error;


    FT_ZERO(bdf);

    error = tt_face_goto_table(face, TTAG_BDF, stream, &length);
    if (error ||
        length < 8 ||
        FT_FRAME_EXTRACT(length, bdf->table))
    {
        error = FT_Err_Invalid_Table;
        goto Exit;
    }

    bdf->table_end = bdf->table + length;

    {
        FT_Byte* p = bdf->table;
        FT_UInt version = FT_NEXT_USHORT(p);
        FT_UInt num_strikes = FT_NEXT_USHORT(p);
        FT_ULong strings = FT_NEXT_ULONG(p);
        FT_UInt count;
        FT_Byte* strike;


        if (version != 0x0001 ||
            strings < 8 ||
            (strings - 8) / 4 < num_strikes ||
            strings + 1 > length)
        {
            goto BadTable;
        }

        bdf->num_strikes = num_strikes;
        bdf->strings = bdf->table + strings;
        bdf->strings_size = length - strings;

        count = bdf->num_strikes;
        p = bdf->table + 8;
        strike = p + count * 4;


        for (; count > 0; count--)
        {
            FT_UInt num_items = FT_PEEK_USHORT(p + 2);

            /*
             *  We don't need to check the value sets themselves, since this
             *  is done later.
             */
            strike += 10 * num_items;

            p += 4;
        }

        if (strike > bdf->strings)
            goto BadTable;
    }

    bdf->loaded = 1;

Exit:
    return error;

BadTable:
    FT_FRAME_RELEASE(bdf->table);
    FT_ZERO(bdf);
    error = FT_Err_Invalid_Table;
    goto Exit;
}


FT_LOCAL_DEF(FT_Error)
tt_face_find_bdf_prop(TT_Face face,
                      const char* property_name,
                      BDF_PropertyRec * aprop)
{
    TT_BDF bdf = &face->bdf;
    FT_Size size = FT_FACE(face)->size;
    FT_Error error = 0;
    FT_Byte* p;
    FT_UInt count;
    FT_Byte* strike;
    FT_Offset property_len;


    aprop->type = BDF_PROPERTY_TYPE_NONE;

    if (bdf->loaded == 0)
    {
        error = tt_face_load_bdf_props(face, FT_FACE(face)->stream);
        if (error)
            goto Exit;
    }

    count = bdf->num_strikes;
    p = bdf->table + 8;
    strike = p + 4 * count;

    error = FT_Err_Invalid_Argument;

    if (size == NULL || property_name == NULL)
        goto Exit;

    property_len = ft_strlen(property_name);
    if (property_len == 0)
        goto Exit;

    for (; count > 0; count--)
    {
        FT_UInt _ppem = FT_NEXT_USHORT(p);
        FT_UInt _count = FT_NEXT_USHORT(p);

        if (_ppem == size->metrics.y_ppem)
        {
            count = _count;
            goto FoundStrike;
        }

        strike += 10 * _count;
    }
    goto Exit;

FoundStrike:
    p = strike;
    for (; count > 0; count--)
    {
        FT_UInt type = FT_PEEK_USHORT(p + 4);

        if ((type & 0x10) != 0)
        {
            FT_UInt32 name_offset = FT_PEEK_ULONG(p);
            FT_UInt32 value = FT_PEEK_ULONG(p + 6);

            /* be a bit paranoid for invalid entries here */
            if (name_offset < bdf->strings_size &&
                property_len < bdf->strings_size - name_offset &&
                ft_strncmp(property_name,
                           (const char*)bdf->strings + name_offset,
                           bdf->strings_size - name_offset) == 0)
            {
                switch (type & 0x0F)
                {
                    case 0x00: /* string */
                    case 0x01: /* atoms */
                        /* check that the content is really 0-terminated */
                        if (value < bdf->strings_size &&
                            ft_memchr(bdf->strings + value, 0, bdf->strings_size))
                        {
                            aprop->type = BDF_PROPERTY_TYPE_ATOM;
                            aprop->u.atom = (const char*)bdf->strings + value;
                            error = 0;
                            goto Exit;
                        }
                        break;

                    case 0x02:
                        aprop->type = BDF_PROPERTY_TYPE_INTEGER;
                        aprop->u.integer = (FT_Int32)value;
                        error = 0;
                        goto Exit;

                    case 0x03:
                        aprop->type = BDF_PROPERTY_TYPE_CARDINAL;
                        aprop->u.cardinal = value;
                        error = 0;
                        goto Exit;

                    default:
                        ;
                }
            }
        }
        p += 10;
    }

Exit:
    return error;
}

#endif /* TT_CONFIG_OPTION_BDF */


/* END */